package github.sf.fw.utils;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Objects;

/**
 * 数组容量变化工具，数组元素删除工具，提供泛型支持
 *
 * @author zhoup
 */
public class ArrayUtil {

    /**
     * 重新构建original数组的大小
     */
    public static <T> Object[] resize(T[] original, int newSize) {
        if (newSize < 0) {
            throw new IllegalArgumentException("newSize can not less than 0. the size is " + newSize);
        }
        Object[] dest = new Object[newSize];
        int size = original.length;
        System.arraycopy(original, 0, dest, 0, Math.min(newSize, size));
        return dest;
    }

    public static <T> T[] resize(T[] original, int newSize, Class<T> type) {
        if (newSize < 0) {
            throw new IllegalArgumentException("newSize can not less than 0. the size is " + newSize);
        }
        T[] dest = createArray(original, newSize, type);
        int size = original.length;
        System.arraycopy(original, 0, dest, 0, Math.min(newSize, size));
        return dest;
    }


    /**
     * 移除original中的deleteIndex索引对应元素
     */
    public static <T> Object[] remove(Object[] original, int deleteIndex) {
        if (original.length == 0) {
            throw new UnsupportedOperationException("array size can't be zero,while delete the array element.");
        }
        if (original.length - 1 <= deleteIndex) {
            throw new UnsupportedOperationException("deleteIndex: " + deleteIndex + " greater than " + (original.length - 1));
        }
        Object[] newArray = new Object[original.length - 1];
        int j = 0;
        for (int i = 0; i < original.length; i++) {
            if (i == deleteIndex) {
                continue;
            }
            newArray[j++] = original[i];
        }
        return newArray;
    }


    public static <T> T[] remove(T[] original, int deleteIndex, Class<T> type) {
        if (original.length == 0) {
            throw new UnsupportedOperationException("array size can't be zero,while delete the array element.");
        }
        if (original.length - 1 <= deleteIndex) {
            throw new UnsupportedOperationException("deleteIndex: " + deleteIndex + " greater than " + (original.length - 1));
        }
        T[] newArray = createArray(original, original.length - 1, type);
        System.arraycopy(original, 0, newArray, 0, deleteIndex);
        System.arraycopy(original, deleteIndex + 1, newArray, deleteIndex, original.length - 1 - deleteIndex);
        return newArray;
    }

    /**
     * 推荐使用的remove方法
     */
    public static <T> T[] remove0(T[] original, int deleteIndex) {
        if (original.length == 0) {
            throw new UnsupportedOperationException("array size can't be zero,while delete the array element.");
        }
        if (original.length - 1 < deleteIndex) {
            throw new UnsupportedOperationException("deleteIndex: " + deleteIndex + " greater than " + (original.length - 1));
        }
        T[] newArray = createArray(original, original.length - 1);
        System.arraycopy(original, 0, newArray, 0, deleteIndex);
        System.arraycopy(original, deleteIndex + 1, newArray, deleteIndex, original.length - 1 - deleteIndex);
        //help gc to work
        original = null;
        return newArray;
    }

    /**
     * 推荐使用的resize方法
     */
    public static <T> T[] resize0(T[] original, int newSize) {
        if (newSize < 1) {
            throw new IllegalArgumentException("newSize can not less than 1. the size is " + newSize);
        }
        T[] dest = createArray(original, newSize);
        int size = original.length;
        System.arraycopy(original, 0, dest, 0, Math.min(newSize, size));
        return dest;
    }


    /**
     * 多次复制的resize方法
     */
    public static <T> T[] resizeWithType(T[] original, int newSize, Class<T> clazz) {
        Class<? extends T[]> arrayClass = getArrayClass(original, clazz);
        Object[] args = resize(original, newSize);
        return Arrays.copyOf(args, newSize, arrayClass);
    }

    /**
     * 多次复制的remove方法
     */
    public static <T> T[] removeWithType(T[] original, int newSize, Class<T> clazz) {
        Class<? extends T[]> arrayClass = getArrayClass(original, clazz);
        Object[] args = remove(original, newSize);
        return Arrays.copyOf(args, newSize, arrayClass);
    }


    ///////////////////////////////////////
    //工具方法

    /**
     * 创建数组
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] createArray(T[] original, int newSize, Class<T> type) {
        return (type == Object[].class)
                ? (T[]) new Object[newSize]
                : (T[]) Array.newInstance(type, newSize);
    }

    /**
     * 创建同<tt>original</tt>类型相同的数组
     *
     * @param newSize 新的数组大小
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] createArray(T[] original, int newSize) {
        Class<T> type = (Class<T>) original[0].getClass();
        return createArray(original, newSize, type);
    }

    /**
     * 获取数组的具体类型
     *
     * @param args 数组
     * @param type 数组中元素类型
     * @param <T>  类型
     * @return 整个数组的类型
     */
    @SuppressWarnings(value = {"unchecked"})
    public static <T> Class<? extends T[]> getArrayClass(Object[] args, Class<T> type) {
        Objects.requireNonNull(args);
        return (Class<? extends T[]>) args.getClass();
    }
}
